/**
 * \file    action.cpp
 * \brief   Parser generator.
 * \author  Jérôme LAURENS
 * \version 0.1
 * \date    September 23, 2014
 *
 * Reads a LALR(1) grammar, generates a parser based on a template file.
 * This is based on lemon parser generator, but has strongly been
 * revisited.
 *
 */

#include <sstream>
#include <iomanip>
#include "symbol.h"
#include "rule.h"
#include "grammar.h"
#include "action.h"
#include "generator.h"
#include "state.h"

LPP_NAMESPACE_BEGIN

/* Print an action to the given file descriptor.  Return FALSE if
** nothing was actually printed.
*/
String Action::description(String::size_type indent, bool showPrecedenceConflicts) const {
    std::ostringstream oss;
    oss << std::string(indent, ' ');
    oss<<lookahead_m->short_name();
    switch (type_m) {
    case Action::SHIFT:
        oss << " SHIFT " << shift_state()->index()<<" / ";
        for (ItemRef const & item: shift_state()->ordered_core_items()) {
            oss << Description()(item)<<" / ";
        }
        break;
    case Action::REDUCE:
        oss << " REDUCE " << reduce_rule()->index()<<" / ";
        oss << Description()(reduce_rule())<<" / ";
        break;
    case Action::ERROR:
        oss << " ERROR / ";
        break;
    case Action::SRCONFLICT:
    case Action::RRCONFLICT:
        oss << " REDUCE " << reduce_rule()->index()<<" / ";
        oss << Description()(reduce_rule())<<" / ";
        break;
    case Action::SSCONFLICT:
        oss << " SHIFT " << shift_state()->index()<<" / ";
        for (ItemRef const & item: shift_state()->core_items()) {
            oss << Description()(item)<<"/";
        }
        oss << " ** Parsing conflict ** / ";
        break;
    case Action::SH_RESOLVED:
        if  (showPrecedenceConflicts) {
            oss << " SHIFT " << shift_state()->index()<<" / ";
            for (ItemRef const & item: shift_state()->core_items()) {
                oss << Description()(item)<<"/";
            }
            oss << " -- dropped by precedence / ";
        }
        break;
    case Action::RD_RESOLVED:
        if (showPrecedenceConflicts) {
            oss << " REDUCE " << reduce_rule()->index()<<" / ";
            oss << Description()(reduce_rule())<<"/";
            oss << " -- dropped by precedence / ";
        }
        break;
    default:
        break;
    }
    return oss.str();
}

/* Compare two actions for sorting purposes.  Return negative, zero, or
** positive if the first action is less than, equal to, or greater than
** the first
*/
int compare(
        Action const & a1,
        Action const & a2
       ) {
    if (a1.lookahead()->index()<a2.lookahead()->index()) return -1;
    if (a1.lookahead()->index()>a2.lookahead()->index()) return  1;
    if ((a1.type()&Action::Type::MASK)<(a2.type()&Action::Type::MASK)) return -1;
    if ((a1.type()&Action::Type::MASK)>(a2.type()&Action::Type::MASK)) return  1;
    if (a1.is_reduce()) {
        if (a1.reduce_rule()->index()<a2.reduce_rule()->index()) return -1;
        if (a1.reduce_rule()->index()>a2.reduce_rule()->index()) return  1;
    } else if (a1.is_shift()) {
        if (a1.shift_state()->index()<a2.shift_state()->index()) return -1;
        if (a1.shift_state()->index()>a2.shift_state()->index()) return  1;
    }
    if (a1.type()<a2.type()) return -1;
    if (a1.type()>a2.type()) return  1;
    return 0;
}
bool operator < (Action const & a1, Action const & a2) {
    return compare(a1,a2)<0;
}
bool operator < (ActionRef const & a1, ActionRef const & a2) {
    return compare_Ref<ActionRef>(a1,a2)<0;
}
bool operator==(ActionRef const & __lhs, ActionRef const & __rhs) {
    return compare_Ref<ActionRef>(__lhs,__rhs)==0;
}
bool operator==(Action const & __lhs, Action const & __rhs) {
    return compare(__lhs,__rhs)==0;
}
ActionCode Action::pre_code() const {
    if (is_shift()) {
        return shift_state()->index();
    } else if(is_reduce()) {
        return reduce_rule()->index();
    } else {
        return 0;
    }
}

void Action::set_default() {
    switch (type_m) {
    case Action::SHIFT:
        lookahead_m = shift_state()->generator().grammar()->default_symbol();
        break;
    case Action::REDUCE:
        lookahead_m = reduce_rule()->grammar().default_symbol();
        break;
    default:
        assert(false);
    }
}

LPP_NAMESPACE_END
